package com.lonni.rpc.core.spring;

import com.lonni.rpc.core.annotation.RpcReference;
import com.lonni.rpc.core.annotation.RpcService;
import com.lonni.rpc.core.context.RpcApplicationContext;
import com.lonni.rpc.core.model.RpcConfig;
import com.lonni.rpc.core.proxy.jdk.JdkDynamicProxy;
import com.lonni.rpc.core.register.ServiceCache;
import com.lonni.rpc.core.register.ServiceRegister;
import com.lonni.rpc.core.register.model.Service;
import com.lonni.rpc.core.register.model.ServiceURL;
import com.lonni.rpc.core.register.zk.ZKUtils;
import com.lonni.rpc.core.socket.server.RpcNettyServer;
import com.lonni.rpc.core.util.IpUtil;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.config.BeanPostProcessor;
import org.springframework.context.ApplicationListener;
import org.springframework.context.event.ContextRefreshedEvent;
import org.springframework.stereotype.Component;

import java.lang.reflect.Field;
import java.util.List;
import java.util.Map;
import java.util.Set;

/**
 * 提供者 spring启动加载
 *
 * @author: Lonni
 * @date: 2024/7/10 0010 17:18
 */
@Slf4j
@Component
public class SpringBeanPostProcessor implements BeanPostProcessor, ApplicationListener<ContextRefreshedEvent> {

    private static String ip = IpUtil.getIp();


    public SpringBeanPostProcessor() {
    }


    /**
     * 后执行
     * 在Bean初始化之前调用
     * 动态设置bean中被RpcService标记的属性
     *
     * @param bean
     * @param beanName
     * @return
     * @throws BeansException
     */
    @Override
    public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
       log.info("postProcessBeforeInitialization,开始检测是否包含RpcService注解");
        //判断类上是否有RpcService 注解
        RpcService rpcService = bean.getClass().getAnnotation(RpcService.class);
        if (rpcService != null) {
            RpcApplicationContext.hasService = true;
            //有注解,拿到实现的接口完全名称,默认拿第一个
            String serviceName = bean.getClass().getInterfaces()[0].getName();
            //存在服务提供者 将服务注册
            Service service = Service.builder()
                    .interfaceName(serviceName)
                    .group(rpcService.group())
                    .version(rpcService.version())
                    .build();
            ServiceURL serviceURL = ServiceURL.builder()
                    .interfaceName(serviceName)
                    .group(rpcService.group())
                    .version(rpcService.version())
                    .ip(ip)
                    .build();
           //上报注册中心且添加到本地缓存
            log.info("包含RpcService注解,注册服务到zk和添加bean到本地缓存中");
            ServiceCache.addService(service, serviceURL);
            ServiceCache.addBean(service,bean);
        }
        return bean;
    }

    /**
     * 设置被RpcReference标记的类
     *
     * @param bean
     * @param beanName
     * @return
     * @throws BeansException
     */
    @Override
    public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
        Class<?> targetClass = bean.getClass();
        Field[] declaredFields = targetClass.getDeclaredFields();
        for (Field declaredField : declaredFields) {
            //判断字段是否有RpcReference注解
            RpcReference rpcReference = declaredField.getAnnotation(RpcReference.class);
            if (rpcReference != null) {
                log.info("存在RpcReference注解标识的类:{}",targetClass.getName());
                RpcApplicationContext.hasReference = true;
                //如果有 拿到当前接口名称
                Service service = Service.builder()
                        .interfaceName(declaredField.getType().getName())
                        .group(rpcReference.group())
                        .version(rpcReference.version())
                        .build();
                //生成动态代理类
                log.info("JdkDynamicProxy生成代理对象");
                JdkDynamicProxy dynamicProxy = new JdkDynamicProxy();
                Object proxyBean = dynamicProxy.getProxy(declaredField.getType(), service);
                declaredField.setAccessible(true);
                //将代理类设置给此属性
                try {
                    declaredField.set(bean, proxyBean);
                } catch (IllegalAccessException e) {
                    e.printStackTrace();
                }
            }
        }
        return bean;
    }

    /**
     * spring容器中所有的bean处理完成后
     *
     * @param event
     */
    @Override
    public void onApplicationEvent(ContextRefreshedEvent event) {
        if (event.getApplicationContext().getParent() == null) {
            RpcConfig config = event.getApplicationContext().getBean(RpcConfig.class);
            if (StringUtils.isNoneBlank(config.getZookeeper().getConnectString()) && !RpcApplicationContext.initZK) {
                log.info("初始化注册中心");
                ZKUtils.me.init(config);
                RpcApplicationContext.initZK = true;
            }
            //判断是否需要启动服务端
            if (RpcApplicationContext.hasService) {
                log.info(RpcApplicationContext.logPrefix + "存在服务实现,开始启动rpc tcp服务!");
                log.info("rpc server启动端口:{}", config.getNetty().getPort());
                new RpcNettyServer(config).run();
                //设置增加的端口
                log.info("rpc server启动完成端口,config:{},context:{}", config.getNetty().getPort(), RpcApplicationContext.serverPort);
                log.info(RpcApplicationContext.logPrefix + "监听到spring启动成功,开始注册服务!");
                ServiceRegister register = event.getApplicationContext().getBean(ServiceRegister.class);
                Set<Map.Entry<Service, List<ServiceURL>>> entries = ServiceCache.SERVICE_URLS.entrySet();
                for (Map.Entry<Service, List<ServiceURL>> entry : entries) {
                    List<ServiceURL> urls = entry.getValue();
                    for (ServiceURL url : urls) {
                        url.setPort(RpcApplicationContext.serverPort);
                        register.register(url);
                    }
                }
                log.info(RpcApplicationContext.logPrefix + "注册服务完成!");
            }
        }
    }
}



